Skip to content

feat(auth): OAuth login with automatic ingestion token provisioning#82

Draft
cpoepke wants to merge 3 commits into
mainfrom
claude/add-oauth-auth-Xy6iv
Draft

feat(auth): OAuth login with automatic ingestion token provisioning#82
cpoepke wants to merge 3 commits into
mainfrom
claude/add-oauth-auth-Xy6iv

Conversation

@cpoepke

@cpoepke cpoepke commented May 17, 2026

Copy link
Copy Markdown
Contributor

TL;DR

  • What & why: users can now run /dash0-agent-plugin:login to authenticate once; the plugin provisions a permanent auth_* ingestion token automatically, so telemetry works without pasting tokens manually.
  • Implementation: OAuth 2.1 PKCE against the Dash0 regional API, then a PUT /api/auth-tokens/claude-code-plugin call (idempotent get-or-create). The short-lived dash0_at_* OAuth token is kept only for token refresh; the ingestion token is what OTLP requests actually use.
  • Notable: depends on dash0#13604 for the CPA endpoint and the allowMembersClaudeCodeAccess org setting.

Overview

Companion PR dash0#13604 (CPA endpoint + org setting + Features UI)
Feature flagged No — gated by allowMembersClaudeCodeAccess org setting (admin-configurable)

Feature: /dash0-agent-plugin:login slash command

Details

What & Why:

  • Before this PR, users had to manually generate an ingestion token in the Dash0 UI and paste it into /plugin Configure. This PR replaces that with a one-command login flow.

Implementation:

  • Browser-based OAuth 2.1 + PKCE against https://api.<region>.aws.dash0.com (the Dash0 regional API). Discovery via RFC 8414 metadata. Dynamic Client Registration (RFC 7591) for loopback redirect URIs.
  • After token exchange, the plugin calls PUT /api/auth-tokens/claude-code-plugin with the OAuth access token. This is an idempotent get-or-create — repeated logins return the same token.
  • The auth_* ingestion token is persisted as IngestionToken in credentials.json. The OTLP hook prefers it over the OAuth access token so OTLP requests bypass the OAuth rotation cycle entirely.
  • Refresh-token rotation keeps the OAuth session alive. RefreshCredentials updates only AuthToken; IngestionToken is never overwritten on refresh (it doesn't expire).
  • Per-user OAuth client registration caches client_id and the bound loopback port in clients.json so repeated logins reuse the same redirect_uri.
  • SessionStart hook emits dash0: connected as <org> on success. The "needs login" branch puts a Skill-tool nudge into additionalContext so Claude proactively prompts the user to run the command.
  • Credentials stored at ~/Library/Application Support/dash0/credentials.json (macOS), $XDG_CONFIG_HOME/dash0/credentials.json (Linux), %AppData%\dash0\credentials.json (Windows), mode 0600.

Feature: Ingestion token provisioning replaces manual token paste

Details

What & Why:

  • The previous approach (MintMachineToken via POST /public/ui/organization/auth-tokens) required admin role and was tied to Clerk JWTs. The new ProvisionIngestionToken uses the standard Dash0 OAuth access token and works for any org member once the admin enables allowMembersClaudeCodeAccess.

Implementation:

  • MintMachineToken, ErrNotAdmin, and deriveCPAURL are deleted — they are no longer used.
  • ProvisionIngestionToken calls PUT /api/auth-tokens/claude-code-plugin (a new endpoint in dash0#13604). The CPA enforces the allowMembersClaudeCodeAccess org setting for non-admin callers.
  • When provisioning fails (e.g. feature not enabled, endpoint not yet deployed), login prints a warning and falls back gracefully — the OAuth token is stored instead and OTLP will work as long as it is valid.
  • OAUTH_CLIENT_ID plugin config option removed (Clerk-specific, no longer needed). AUTH_URL description updated to reflect the regional API URL pattern.

Checklist

Details
  • I have tested these changes locally and in the PR build.
  • I have reviewed my own code changes.
  • I have added any relevant unit + e2e tests to ensure my code is robust.
  • I have made relevant comments and updated the documentation to reflect the changes made.
  • I have created a Chromatic build and reviewed the results. (no UI changes in this repo)
  • I have checked for any potential security vulnerabilities and addressed them.
  • I have considered the impact on performance and optimized the code where necessary.
  • If code is behind a feature flag, I've ensured the existing code is not broken. (not flagged)
  • My code follows the project's coding standards and style guidelines.

@cpoepke cpoepke changed the title feat: add OAuth PKCE login flow + region selector feat: add /dash0-agent-plugin:login OAuth PKCE flow May 17, 2026
@cpoepke cpoepke marked this pull request as draft May 17, 2026 22:33
@cpoepke cpoepke changed the title feat: add /dash0-agent-plugin:login OAuth PKCE flow feat(auth): Clerk OAuth login flow + refresh tokens May 17, 2026
cpoepke added 2 commits May 18, 2026 00:39
Adds a browser-based sign-in flow so users can authenticate the Dash0
Claude Code plugin once and have it emit telemetry on their behalf.

- /dash0-agent-plugin:login slash command runs OAuth 2.0 + PKCE against
  Clerk (Dash0's identity provider). Discovery via RFC 8414 metadata,
  PKCE S256. Defaults to https://clerk.dash0.com; auto-selects the
  dev tenant when DASH0_OTLP_URL points at a .dash0-dev.com host.

- Credentials are persisted to the OS user-config dir (mode 0600):
    ~/Library/Application Support/dash0/credentials.json (macOS)
    $XDG_CONFIG_HOME/dash0/credentials.json (Linux)
    %AppData%\dash0\credentials.json (Windows)

- After OAuth, the plugin uses the issued Clerk JWT to mint a long-lived
  auth_* token via CPA POST /public/ui/organization/auth-tokens. Falls
  back to the short-lived OAuth access_token when mint fails (e.g. dev
  envs without admin role).

- Refresh-token rotation: offline_access scope yields a refresh token;
  SessionStart hook auto-refreshes on 401, re-mints, persists, retries
  CheckConnectivity, and reports the rotated state to the user.

- SessionStart printHookResponse always emits a status line:
    - dash0: telemetry is not active        (no OTLP_URL)
    - dash0: not authenticated              (no token)
    - dash0: auth token rejected            (401 after refresh)
    - dash0: connected as <org-id>          (valid)
  The "needs login" branches put a Skill-tool nudge into
  additionalContext so Claude proactively invokes the slash command
  before continuing with the user's request.

- Per-user OAuth client registration caches client_id + the bound
  loopback port in clients.json so repeat logins reuse the same
  redirect_uri (Dash0 / Clerk enforce exact match).

- New userConfig entries: AUTH_URL, OAUTH_CLIENT_ID (plus env-var
  fallbacks DASH0_AUTH_URL, DASH0_OAUTH_CLIENT_ID).

- Drops the original region selector (us / eu / dev) and the
  DASH0_REGION env var — the URL-only model matches Dash0's real
  topology (Clerk for OAuth, CPA for mint, regional API for ingest).
  Credentials now carry AuthURL, ClientID, RefreshToken instead of
  Region/APIBase. Clients map keyed by AuthURL.

- CI adds an auth-e2e job that runs the mocked flow without an
  ANTHROPIC_API_KEY secret (works for fork PRs).

- 14 e2e scenarios + 160 unit tests cover discover/register/exchange/
  mint/refresh paths against an in-process mock Clerk-shaped server.
Pure formatting (struct-tag alignment). No behavior changes.
@cpoepke cpoepke force-pushed the claude/add-oauth-auth-Xy6iv branch from c059ddd to 1755279 Compare May 17, 2026 22:40
Replace the Clerk-specific MintMachineToken path (POST /public/ui/organization/auth-tokens)
with a call to PUT /api/auth-tokens/claude-code-plugin — the new idempotent get-or-create
endpoint added to the CPA. This removes the admin-only constraint: any org member with
allowMembersClaudeCodeAccess=true can now log in and get a permanent ingestion token.

Changes:
- Add ProvisionIngestionToken() calling PUT /api/auth-tokens/claude-code-plugin
- Add IngestionToken field to Credentials; OTLP hook prefers it over AuthToken
- Remove MintMachineToken, ErrNotAdmin, deriveCPAURL (all obsolete)
- Remove Clerk-specific ClientID/Scope login options and OAUTH_CLIENT_ID plugin config
- RefreshCredentials no longer attempts to re-mint; ingestion token persists across refreshes
@cpoepke cpoepke changed the title feat(auth): Clerk OAuth login flow + refresh tokens feat(auth): OAuth login with automatic ingestion token provisioning Jun 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant